#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/queue.h>
#include <string.h>
#include <el.h>
#include "vty.h"
#include "profile.h"

#define BP_FILE_NAME_MAX 215

static char bp_file[BP_FILE_NAME_MAX];

static TAILQ_HEAD(bp_head, bprofile) bp_head = TAILQ_HEAD_INITIALIZER(bp_head);
static TAILQ_HEAD(bp_order, bprofile) bp_order = TAILQ_HEAD_INITIALIZER(bp_order);

static void set_bp_file(const char *name)
{
  if (name == NULL || name[0] == 0)
    snprintf(bp_file, BP_FILE_NAME_MAX - 1, "./bp_file");
  else
    snprintf(bp_file, BP_FILE_NAME_MAX - 1, "%s", name);
}


int cmd_load_profile(void *desc, const char *fmt, ...)
{
   int cnt;
   va_list argptr;
   FILE *fp = (FILE *) desc;
   
   va_start(argptr, fmt);
   fprintf(fp, "\n");
   cnt = vfprintf(fp, fmt, argptr);
   va_end(argptr);
   return cnt;
}

static void do_profile(void *file)
{
  struct bprofile *p;
  TAILQ_FOREACH(p, &bp_order,entry) {
    p->func(file);
  }
}

void register_profile(struct bprofile *prof)
{
  TAILQ_INSERT_TAIL(&bp_head, prof, entry);
}

static bool profile_after(struct bprofile *n0,struct bprofile *n1)
{
  const char *p0 = n1->after;
  while ((*p0) != 0) {
    const char *p1 = p0;
    for (p1=p0;(*p1) != 0 && (*p1) != ','; p1++)
      ;
    int len = p1 - p0;
    if (len == 0)
      break;
    if (!strcmp(n0->name, "debug"))
      return true;
    if (len == strlen(n0->name) && !strncmp(n0->name, p0, len)) {
      return true;
    }
    p0 = p1;
    if ((*p1) == ',')
      p0++; 
  }
  return false;
}

static void sort_profile(void)
{
  struct bprofile *mark = NULL;
  while (!TAILQ_EMPTY(&bp_head)) {
    struct bprofile *n = TAILQ_FIRST(&bp_head);
    TAILQ_REMOVE(&bp_head, n, entry);
    if (mark == n)
      break;
    if (mark == NULL)
      mark = n;
    struct bprofile *lookup;
    TAILQ_FOREACH(lookup, &bp_head,entry) {
      if (profile_after(n, lookup)){
        break;
      }
    }
    if (lookup != NULL) {
      TAILQ_INSERT_TAIL(&bp_head, n, entry);
    } else {
      TAILQ_INSERT_HEAD(&bp_order, n, entry);
      mark = NULL;
    }
  }
}

void init_profile(const char *name)
{
  sort_profile();
	set_bp_file(name);
  FILE  *fp = fopen(bp_file, "r");
  if (fp == NULL) 
    return;
  int savefd=-1,fd = open("/dev/null", O_WRONLY);
  if (fd >= 0){
    savefd = dup(STDOUT_FILENO);
    dup2(fd, STDOUT_FILENO);
  }   
  char buf[1024];
  while (fgets(buf, sizeof(buf), fp) != NULL) {
    int len = strlen(buf);
    if (len > 0 && buf[0] != '#' && len < (int)sizeof(buf))  {
      buf[len] = '\n';
      vty_read(buf, len + 1);
    }
  }
  if (fd >= 0) {
    dup2(savefd, STDOUT_FILENO); 
    close(savefd);
    close(fd);
  }
  fclose(fp);
}

BOLT(save_profile, "cmd profile save")
{
   FILE *fp = fopen(bp_file, "w+");
   if (fp != NULL) {
      do_profile((void*)fp);
      fclose(fp);
   }
}

BOLT(remove_profile, "cmd profile remove")
{
   FILE *fp = fopen(bp_file, "w+");
   if (fp != NULL) 
      fclose(fp);
}


BOLT(running_profile, "cmd profile running")
{
  do_profile((void*)stdout);
}

BOLT(show_profile, "cmd profile show")
{
  struct bprofile *p;
  printf ("resolved profile  :\n");
  TAILQ_FOREACH(p, &bp_order,entry) {
    printf ("  %12s  <------ %s\n", p->name, p->after);
  }
  printf ("unresolved profile:\n");
  TAILQ_FOREACH(p, &bp_head,entry) {
    printf ("  %12s  <------ %s\n", p->name, p->after);
  }
}
